﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using UtilZ.Dotnet.Ex.Base;
using UtilZ.Dotnet.Ex.Model;

namespace UtilZ.Dotnet.Ex.Normalization
{
#pragma warning disable 8500
    unsafe internal abstract class InnerNormalizerAbs<TSource, TResult> : IInnerNormalizer<TSource, TResult>
#if CORE
        where TSource : struct, IComparable<TSource>, INumberBase<TSource>
        where TResult : struct, IComparable<TResult>, INumberBase<TResult>
#else
        where TSource : struct, IComparable<TSource>
        where TResult : struct, IComparable<TResult>
#endif
    {
        private readonly List<double> _slidMinValueList = null;
        private readonly List<double> _slidMaxValueList = null;
        private double? _historyMin = null;
        private double? _historyMax = null;
        private readonly int? _slidFrames = null;


        private readonly NormalizerOptions _options;
        /// <summary>
        /// 归一化选项参数
        /// </summary>
        public NormalizerOptions Options
        {
            get { return this._options; }
        }

        private readonly bool _useMap;

        private readonly double _pre;
        private readonly double _outputMin;
        private readonly double _outputMax;
        private readonly double _outputOffset;
        protected readonly TResult _defaultValue;


        private readonly TResult[] _mapResultArr;
        private readonly long _mapIndexOffset;

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="options">归一化选项参数</param>
        /// <param name="useMap">是否使用映射</param>
        public InnerNormalizerAbs(NormalizerOptions options, bool useMap)
        {
            if (!DoubleEx.Valid(options.OutputDataMin))
            {
                throw new ArgumentException("归一化结果最小值无效", nameof(options.OutputDataMin));
            }

            if (!DoubleEx.Valid(options.OutputDataMax))
            {
                throw new ArgumentException("归一化结果最大值无效", nameof(options.OutputDataMax));
            }

            if (options.OutputDataMin > options.OutputDataMax)
            {
                throw new ArgumentException("归一化结果最小值大于了最大值");
            }


            this._useMap = useMap;
            this._outputMin = options.OutputDataMin;
            this._outputMax = options.OutputDataMax;
#if CORE
            this._defaultValue = TResult.CreateChecked((options.OutputDataMin + options.OutputDataMax) / 2);
#else
                
            this._defaultValue = (TResult)ConvertEx.ConvertToObject(typeof(TResult), (options.OutputDataMin + options.OutputDataMax) / 2);
#endif
            this._outputOffset = (options.OutputDataMin + options.OutputDataMax) / 2;
            this._pre = options.Pre;
            this._options = options;

            if (useMap)
            {
                //映射法
                this._historyMin = options.InputDataMin.Value;
                this._historyMax = options.InputDataMax.Value;
                this._mapResultArr = BuildMapResult(options, out this._mapIndexOffset);
            }
            else
            {
                //实时计算

                if (options.HasInputRange())
                {
                    this._historyMin = options.InputDataMin.Value;
                    this._historyMax = options.InputDataMax.Value;
                }
                else
                {
                    this._slidFrames = options.SlidFrames;
                    if (options.SlidFrames > 0)
                    {
                        this._slidMinValueList = new List<double>(options.SlidFrames);
                        this._slidMaxValueList = new List<double>(options.SlidFrames);
                    }
                }
            }
        }

        private TResult[] BuildMapResult(NormalizerOptions options, out long indexOffset)
        {
            int count = (int)(options.InputDataMax.Value - options.InputDataMin.Value) + 1;
            if (count > int.MaxValue)
            {
                throw new InvalidOperationException($"归一化输入数据范围大于了{int.MaxValue},不支持映射模式,请使用实时计算模式.");
            }

            indexOffset = (long)options.InputDataMin.Value;
            var source = new TSource[count];
            long value = indexOffset;

            for (int i = 0; i < count; i++)
            {
#if CORE
                source[i] = TSource.CreateChecked(value++);
#else
                source[i] = (TSource)ConvertEx.ConvertToObject(typeof(TSource), value++);
#endif
            }

            var dest = new TResult[count];
            fixed (TResult* destPtr = dest)
            {
                fixed (TSource* sourcePtr = source)
                {
                    this.PrimitiveExcute(sourcePtr, destPtr, count, this._outputOffset);
                }
            }

            return dest;
        }

        private static TResult[] BuildMapResult_bk(NormalizerOptions options, out int indexOffset)
        {
            int count = (int)(options.InputDataMax.Value - options.InputDataMin.Value);
            if (count <= 1)
            {
                throw new ArgumentException($"归一化输入数据范围{options.InputDataMax.Value}-{options.InputDataMin.Value}小于了2,不支持映射模式,请使用实时计算模式.");
            }
            else if (count > int.MaxValue)
            {
                throw new InvalidOperationException($"归一化输入数据范围大于了{int.MaxValue},不支持映射模式,请使用实时计算模式.");
            }

            indexOffset = (int)options.InputDataMin.Value;
            if (indexOffset < 0)
            {
                indexOffset = 0 - indexOffset;
            }

            var resultArr = new TResult[count];
            double factor = (options.OutputDataMax - options.OutputDataMin) / (count - 1);
            double temp;

            for (var i = 0; i < resultArr.Length; i++)
            {
                temp = i * factor + options.OutputDataMin;

                if (temp < options.OutputDataMin)
                {
                    temp = options.OutputDataMin;
                }
                else if (temp > options.OutputDataMax)
                {
                    temp = options.OutputDataMax;
                }
#if CORE
                resultArr[i] = TResult.CreateChecked(temp);
#else
                resultArr[i] = (TResult)ConvertEx.ConvertToObject(typeof(TResult), temp);
#endif
            }

            return resultArr;
        }





        protected TResult ConvertToResult(double value)
        {
            if (DoubleEx.Valid(value))
            {
#if CORE
                if (value < this._outputMin)
                {
                    value = this._outputMin;
                }
                else if (value > this._outputMax)
                {
                    value = this._outputMax;
                }

                return TResult.CreateChecked(value);
#else
                if (value.CompareTo(this._outputMin) < 0)
                {
                    value = this._outputMin;
                }
                else if (value.CompareTo(this._outputMax) > 0)
                {
                    value = this._outputMax;
                }
                return (TResult)ConvertEx.ConvertToObject(typeof(TResult), value);
#endif
            }
            else
            {
                return this._defaultValue;
            }
        }


        /// <summary>
        /// 重置归一化器,恢复至构造初始状态
        /// </summary>
        public void Reset()
        {
            if (this._useMap)
            {

            }
            else
            {
                if (this._slidFrames.HasValue)
                {
                    if (this._slidFrames.Value > 0)
                    {
                        this._slidMinValueList.Clear();
                        this._slidMaxValueList.Clear();
                    }
                    else if (this._slidFrames.Value == 0)
                    {

                    }
                    else
                    {
                        this._historyMin = null;
                        this._historyMax = null;
                    }
                }
                else
                {

                }
            }
        }

        /// <summary>
        /// 获取归一化系数
        /// </summary>
        /// <param name="minValue">数据源数据中的最小值</param>
        /// <param name="maxValue">数据源数据中的最大值</param>
        /// <param name="inputOffset">输入数据偏移量</param>
        /// <returns>归一化系数</returns>
        public double CaculateNormalizeFactor(double minValue, double maxValue, out double inputOffset)
        {
            inputOffset = (double)(minValue + maxValue) / 2;

            if (this._slidFrames.HasValue)
            {
                if (this._slidFrames.Value > 0)
                {
                    // 大于0时为保留最近的平滑帧数内的最小最大值做归一化;

                    this._slidMinValueList.Add(minValue);
                    this._slidMaxValueList.Add(maxValue);

                    while (this._slidMinValueList.Count > this._slidFrames.Value)
                    {
                        this._slidMinValueList.RemoveAt(0);
                        this._slidMaxValueList.RemoveAt(0);
                    }

                    minValue = this._slidMinValueList.Min();
                    maxValue = this._slidMaxValueList.Max();
                }
                else if (this._slidFrames.Value == 0)
                {
                    //等于0表示不作平滑,根据每帧中的最大最小值做归一化;
                }
                else
                {
                    // 小于表示取历史上最大最小值做归一化.

                    if (this._historyMin == null)
                    {
                        this._historyMin = minValue;
                        this._historyMax = maxValue;
                    }
                    else
                    {
                        if (minValue < this._historyMin.Value)
                        {
                            this._historyMin = minValue;
                        }
                        else
                        {
                            minValue = this._historyMin.Value;
                        }

                        if (maxValue > this._historyMax.Value)
                        {
                            this._historyMax = maxValue;
                        }
                        else
                        {
                            maxValue = this._historyMax.Value;
                        }
                    }
                }
            }
            else
            {
                minValue = this._historyMin.Value;
                maxValue = this._historyMax.Value;
            }

            var range = maxValue - minValue;
            if (Math.Abs(range) < this._pre)
            {
                return double.NaN;
            }
            else
            {
                return (this._outputMax - this._outputMin) / range;
            }
        }

        /// <summary>
        /// 执行单个值归一化
        /// </summary>
        /// <param name="source">归一化原始数据</param>
        /// <param name="factor">归一化系数,当为映射时此参数不使用,填任意值</param>
        /// <param name="inputOffset">输入数据偏移量,当为映射时此参数不使用,填任意值</param>
        /// <returns>单个值归一化结果</returns>
        public TResult Excute(TSource source, double factor, double inputOffset)
        {
            if (this._useMap)
            {
                return this.PrimitiveExcuteMap(source, this._mapResultArr, this._mapIndexOffset);
            }
            else
            {
                if (double.IsNaN(factor))
                {
                    return this._defaultValue;
                }
                else
                {
                    return this.PrimitiveExcute(source, factor, inputOffset, this._outputOffset);
                }
            }
        }

        protected abstract TResult PrimitiveExcute(TSource source, double factor, double inputOffset, double outputOffset);

        protected virtual TResult PrimitiveExcuteMap(TSource source, TResult[] resultArr, long indexOffset)
        {
            throw new NotSupportedException();
        }



        /// <summary>
        /// 执行归一化,当原始数据长度与结果数据不一致时,取较小值为长度
        /// </summary>
        /// <param name="source">归一化原始数据</param>
        /// <param name="dest">归一化结果数据</param>
        public void Excute(Span<TSource> source, Span<TResult> dest)
        {
#if NET4_0
            TSource* sourcePtr = source.DataPtr;
            TResult* destPtr = dest.DataPtr;
#else
            TSource* sourcePtr = (TSource*)System.Runtime.CompilerServices.Unsafe.AsPointer<TSource>(ref source[0]);
            TResult* destPtr = (TResult*)System.Runtime.CompilerServices.Unsafe.AsPointer<TResult>(ref dest[0]);
#endif
            if (this._useMap)
            {
                this.PrimitiveExcuteMap(sourcePtr, destPtr, Math.Min(source.Length, dest.Length), this._mapResultArr, this._mapIndexOffset);
            }
            else
            {
                this.PrimitiveExcute(sourcePtr, destPtr, Math.Min(source.Length, dest.Length), this._outputOffset);
            }
        }

        /// <summary>
        /// 执行归一化,当原始数据长度与结果数据不一致时,取较小值为长度
        /// </summary>
        /// <param name="source">归一化原始数据</param>
        /// <param name="dest">归一化结果数据</param>
        public void Excute(SpanZ<TSource> source, SpanZ<TResult> dest)
        {
            if (this._useMap)
            {
                this.PrimitiveExcuteMap(source.DataPtr, dest.DataPtr, Math.Min(source.Length, dest.Length), this._mapResultArr, this._mapIndexOffset);
            }
            else
            {
                this.PrimitiveExcute(source.DataPtr, dest.DataPtr, Math.Min(source.Length, dest.Length), this._outputOffset);
            }
        }

        protected abstract void PrimitiveExcute(TSource* sourcePtr, TResult* destPtr, int length, double offset);

        protected virtual void PrimitiveExcuteMap(TSource* sourcePtr, TResult* destPtr, int length, TResult[] resultArr, long indexOffset)
        {
            throw new NotSupportedException();
        }


        protected void FactorNaNFill(TResult* destPtr, int length)
        {
            for (int i = 0; i < length; i++)
            {
                destPtr[i] = this._defaultValue;
            }
        }

    }
}
